/*
 * Este programa es software libre; usted puede redistribuirlo y/o modificarlo bajo los terminos
 * de la licencia "GNU General Public License" publicada por la Fundacion "Free Software Foundation".
 * Este programa se distribuye con la esperanza de que pueda ser util, pero SIN NINGUNA GARANTIA;
 * vea la licencia "GNU General Public License" para obtener mas informacion.
 */
package adalid.util.sql;

import adalid.commons.TLB;
import adalid.commons.interfaces.Programmer;
import adalid.commons.properties.PropertiesHandler;
import adalid.commons.util.FilUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * @author Jorge Campins
 */
public class SqlUtil {

    private static final Logger logger = Logger.getLogger(SqlUtil.class);

    private static final Properties bootstrapping = PropertiesHandler.getBootstrapping();

    static String highlight(String text) {
        return "WATCH OUT! " + text;
    }

    // <editor-fold defaultstate="collapsed" desc="instance fields">
    protected String _dbms;

    protected String _host;

    protected String _port;

    protected String _user;

    protected String _password;

    protected String _database;

    protected String _schema;

    protected String _urlPattern;

    protected String _driver;

    protected String _programmer;

    protected String _url;

    protected Connection _connection;

    protected boolean _initialised;
    // </editor-fold>

    public SqlUtil(String[] args) {
        _initialised = true;
        _initialised = _initialised && dbms(args);
        _initialised = _initialised && host(args);
        _initialised = _initialised && port(args);
        _initialised = _initialised && user(args);
        _initialised = _initialised && password(args);
        _initialised = _initialised && database(args);
        _initialised = _initialised && schema(args);
    }

    // <editor-fold defaultstate="collapsed" desc="args">
    private boolean dbms(String[] args) {
        _dbms = arg(0, args).toLowerCase();
        if (StringUtils.isNotBlank(_dbms)) {
            _urlPattern = bootstrapping.getProperty(_dbms + ".string");
            _driver = bootstrapping.getProperty(_dbms + ".driver");
            _programmer = bootstrapping.getProperty(_dbms + ".programmer");
            if (StringUtils.isNotBlank(_urlPattern) && StringUtils.isNotBlank(_driver) && StringUtils.isNotBlank(_programmer)) {
                Object object = getNewInstanceForName(_programmer);
                if (object instanceof Programmer) {
                    TLB.setProgrammer(_dbms, (Programmer) object);
                    logValidArgument("dbms", _dbms);
                    return true;
                }
            }
        }
        logInvalidArgument("dbms", _dbms);
        logSyntaxError();
        return false;
    }

    private boolean host(String[] args) {
        _host = arg(1, args);
        if (StringUtils.isNotBlank(_host)) {
            logValidArgument("host", _host);
            return true;
        }
        logInvalidArgument("host", _host);
        logSyntaxError();
        return false;
    }

    private boolean port(String[] args) {
        _port = arg(2, args);
        if (StringUtils.isNotBlank(_port)) {
            logValidArgument("port", _port);
            return true;
        }
        logInvalidArgument("port", _port);
        logSyntaxError();
        return false;
    }

    private boolean user(String[] args) {
        _user = arg(3, args);
        if (StringUtils.isNotBlank(_user)) {
            logValidArgument("user", _user);
            return true;
        }
        logInvalidArgument("user", _user);
        logSyntaxError();
        return false;
    }

    private boolean password(String[] args) {
        _password = arg(4, args);
        if (StringUtils.isNotBlank(_password)) {
//          logValidArgument("password", _password);
            return true;
        }
        logInvalidArgument("password", _password);
        logSyntaxError();
        return false;
    }

    private boolean database(String[] args) {
        _database = arg(5, args);
        if (StringUtils.isNotBlank(_database)) {
            logValidArgument("database", _database);
            return true;
        }
        logInvalidArgument("database", _database);
        logSyntaxError();
        return false;
    }

    private boolean schema(String[] args) {
        _schema = arg(6, args);
        if (StringUtils.isNotBlank(_schema)) {
            logValidArgument("schema", _schema);
            return true;
        }
        logInvalidArgument("schema", _schema);
        logSyntaxError();
        return false;
    }

    protected String arg(int i, String[] args) {
        return args != null && args.length > i && args[i] != null ? args[i].trim() : "";
    }

    protected void logValidArgument(String param, String arg) {
        logger.info(param + " = \"" + arg + "\" ");
    }

    protected void logInvalidArgument(String param, String arg) {
        logger.error(param + " \"" + arg + "\" " + "is missing or invalid");
    }

    protected void logSyntaxError() {
        logger.error("Syntax: " + getClass().getSimpleName() + " dbms, host, port, user, password, database, schema");
    }

    protected Object getNewInstanceForName(String className) {
        Class<?> clazz = getClassForName(className);
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException ex) {
            throw new RuntimeException(ex);
        }
    }

    protected Class<?> getClassForName(String className) {
        if (StringUtils.isBlank(className)) {
            return null;
        }
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(ex);
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="connect & close">
    protected boolean connect() {
        logger.info("connect");
        _url = _urlPattern;
        _url = _url.replace("%dbhost%", _host);
        _url = _url.replace("%dbport%", _port);
        _url = _url.replace("%dbuser%", _user);
        _url = _url.replace("%dbpass%", _password);
        _url = _url.replace("%dbname%", _database);
        try {
            Class.forName(_driver);
            _connection = DriverManager.getConnection(_url, _user, _password);
            logger.info(_url + " connection opened");
            return true;
        } catch (ClassNotFoundException | SQLException ex) {
            logger.fatal(_url, ex);
        }
        _connection = null;
        return false;
    }

    protected boolean close() {
        logger.info("close");
        if (_connection != null) {
            try {
                if (!_connection.isClosed()) {
                    _connection.close();
                }
                logger.info(_url + " connection closed ");
                _connection = null;
                _url = null;
                return true;
            } catch (SQLException ex) {
                logger.fatal(_url, ex);
            }
        }
        return false;
    }

    protected void close(PreparedStatement statement) {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException ex) {
                logger.fatal(statement, ex);
            }
        }
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="getters">
    public String getDbms() {
        return _dbms;
    }

    public String getHost() {
        return _host;
    }

    public String getPort() {
        return _port;
    }

    public String getUser() {
        return _user;
    }

    public String getPassword() {
        return _password;
    }

    public String getDatabase() {
        return _database;
    }

    public String getSchema() {
        return _schema;
    }

    public String getDriver() {
        return _driver;
    }

    public String getUrl() {
        return _url;
    }

    public boolean isInitialised() {
        return _initialised;
    }

    public String getMetajavaPath() {
        return FilUtils.fixPath(bootstrapping.getProperty(("metajava.path")));
    }

    public String getSourceMetajavaPackage() {
        return bootstrapping.getProperty("commons.package.name");
    }

    public String getTargetMetajavaPackage() {
        return bootstrapping.getProperty("default.package.name") + "." + _database.toLowerCase();
    }
    // </editor-fold>

}
